{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "6d2e4af3",
   "metadata": {},
   "outputs": [],
   "source": [
    "# This is a sample Python script.\n",
    "import os\n",
    "import re\n",
    "from datetime import timedelta\n",
    "import pandas as pd\n",
    "\n",
    "HOUR = 60 * 60 * 1000\n",
    "MINUTE = 60 * 1000\n",
    "\n",
    "# 找rtc最相关的wakeup alarm\n",
    "#\n",
    "file_id = 1\n",
    "all_attri_num = pd.DataFrame()\n",
    "\n",
    "# 不同策略下的数据\n",
    "# 不同策略下的数据\n",
    "networks = [\"flight\",\"wifi\",\"lte\"]\n",
    "strategies_list = [\"fullAlign\",\"align\",\"paper\",\"origin\"]\n",
    "network_data = {\"flight\":{},\"wifi\":{},\"lte\":{}}\n",
    "for n in networks:\n",
    "    tmp = {}\n",
    "    for s in strategies_list:\n",
    "        tmp[s] = {\"rtc_alarm\": [], \"drain\": []}\n",
    "    network_data[n] = tmp\n",
    "\n",
    "stratgies = {\n",
    "    \"fullAlign\": {\"rtc_alarm\": [], \"drain\": []},\n",
    "    \"align\": {\"rtc_alarm\": [], \"drain\": []},\n",
    "    \"paper\": {\"rtc_alarm\": [], \"drain\": []},\n",
    "    \"origin\": {\"rtc_alarm\": [], \"drain\": []}\n",
    "}\n",
    "\n",
    "# 将时间字符串转换为毫秒\n",
    "def parse_time(time_str):\n",
    "    h_match = re.search(r'(?P<hour>\\d+)h\\d+', time_str)\n",
    "    m_match = re.search(r'(?P<minute>\\d+)m\\d+', time_str)\n",
    "    s_match = re.search(r'(?P<second>\\d+)s\\d+', time_str)\n",
    "    ms_match = re.search(r'(?P<millisecond>\\d+)ms', time_str)\n",
    "\n",
    "    h = int(h_match.group('hour')) if h_match else 0\n",
    "    m = int(m_match.group('minute')) if m_match else 0\n",
    "    s = int(s_match.group('second')) if s_match else 0\n",
    "    ms = int(ms_match.group('millisecond')) if ms_match else 0\n",
    "\n",
    "    total_ms = h * 60 * 60 * 1000 + m * 60 * 1000 + s * 1000 + ms\n",
    "    if total_ms == 0:\n",
    "        return None\n",
    "    else:\n",
    "        return total_ms\n",
    "\n",
    "def get_time(time_str):\n",
    "    h_match = re.search(r'(?P<hour>\\d+)h\\d+', time_str)\n",
    "    m_match = re.search(r'(?P<minute>\\d+)m\\d+', time_str)\n",
    "    s_match = re.search(r'(?P<second>\\d+)s\\d+', time_str)\n",
    "    ms_match = re.search(r'(?P<millisecond>\\d+)ms', time_str)\n",
    "\n",
    "    h = int(h_match.group('hour')) if h_match else 0\n",
    "    m = int(m_match.group('minute')) if m_match else 0\n",
    "    s = int(s_match.group('second')) if s_match else 0\n",
    "    ms = int(ms_match.group('millisecond')) if ms_match else 0\n",
    "\n",
    "\n",
    "    return timedelta(hours=int(h),minutes=int(m), seconds=int(s), milliseconds=int(ms))\n",
    "\n",
    "# 处理每一行数据\n",
    "def process_drain_line(line):\n",
    "    screen_off_time = None\n",
    "    screen_on_time = None\n",
    "    charge_on_screen = None\n",
    "\n",
    "    time = parse_time(line.split()[1])\n",
    "\n",
    "    charge_search = re.search(r'charge=(\\d+)', line)\n",
    "    if charge_search:\n",
    "        charge = int(charge_search.group(1))\n",
    "\n",
    "    if \"-screen\" in line and screen_off_time is None:\n",
    "        screen_off_time = time\n",
    "\n",
    "    if \"+screen\" in line:\n",
    "        screen_on_time = time\n",
    "        charge_on_screen = charge\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "def parse_log_line(line):\n",
    "    line = line.lstrip()\n",
    "    match = re.search(r\"(\\d+ms).*?(wake_reason=0:\\\"\\d+ qpnp_rtc_alarm\\\"|\\\"\\*walarm\\*:|\\\"\\*alarm\\*:).*\",\n",
    "                      line)\n",
    "    if match:\n",
    "        return (get_time(line), match.group(2))\n",
    "\n",
    "    return None\n",
    "\n",
    "def parse_log(path):\n",
    "    f = open(path, encoding=\"utf-8\")\n",
    "    fileContent = f.readlines()\n",
    "    f.close()\n",
    "    return fileContent\n",
    "\n",
    "def parse_timestamp(timestamp_str):\n",
    "    m, s, ms = re.match(r\"\\+(\\d+)m(\\d+)s(\\d+)ms\", timestamp_str).groups()\n",
    "    return timedelta(minutes=int(m), seconds=int(s), milliseconds=int(ms))\n",
    "\n",
    "def is_less_than_half_second(t1, t2):\n",
    "    return abs(t1 - t2) < timedelta(seconds=0.5)\n",
    "\n",
    "def count_alarm_walarm(line):\n",
    "    alarm = 0\n",
    "    walarm = 0\n",
    "    line = line.lstrip()\n",
    "    if re.search(r':\"\\*walarm\\*:',line):\n",
    "        walarm = 1\n",
    "    if re.search(r':\"\\*alarm\\*:',line):\n",
    "        alarm = 1\n",
    "    return walarm,alarm\n",
    "\n",
    "def get_rtc_alarm_num(line):\n",
    "    match = re.search(r'.*qpnp_rtc_alarm:.*(\\d+)\\s*times', line)\n",
    "    if match:\n",
    "        qpnp_rtc_alarm = int(match.group(1))\n",
    "    return qpnp_rtc_alarm\n",
    "\n",
    "\n",
    "def count_event(content):\n",
    "    batteryContentFlag = False\n",
    "    batteryContentOutFlag = False\n",
    "\n",
    "    wakeupContentFlag = False\n",
    "    wakeupContentOutFlag = False\n",
    "    # qpnp_rtc_alarm出现wakelock为walarm或者alarm次数\n",
    "    walarm_rtc_count = 0\n",
    "    alarm_rtc_count = 0\n",
    "\n",
    "    # wakelock为walarm或者alarm次数\n",
    "    w_count = 0\n",
    "    a_count = 0\n",
    "\n",
    "    atc_drain = 0\n",
    "    duration = \"0\"\n",
    "    qpnp_rtc_alarm = 0\n",
    "    app_wakeup_alarm_num = 0\n",
    "\n",
    "    prev_timestamp = None\n",
    "\n",
    "    # 计算真实耗电相关变量\n",
    "    time = None\n",
    "    screen_off_time = None\n",
    "    screen_on_time = None\n",
    "    charge_start = None\n",
    "    charge_end = None\n",
    "    charge_time = None\n",
    "    charge_start_flag = False\n",
    "\n",
    "    # time pattern\n",
    "\n",
    "    app_wakeup_num = 0\n",
    "    app_wakeup = pd.DataFrame()\n",
    "\n",
    "    for line in content:\n",
    "        # Judge arrive batterystats paragraph\n",
    "        line = line.lstrip()\n",
    "        if not batteryContentFlag:\n",
    "            if re.search(r\"DUMP OF SERVICE batterystats:.*\", line):\n",
    "                batteryContentFlag = True\n",
    "\n",
    "        # Judge out batterystats paragraph\n",
    "        if (batteryContentOutFlag is False) and (batteryContentFlag is True):\n",
    "            if re.search(r\"Per-PID Stats:.*\", line):\n",
    "                batteryContentOutFlag = True\n",
    "                batteryContentFlag = False\n",
    "\n",
    "        # Judge arrive app status\n",
    "        if (batteryContentOutFlag is True) and (wakeupContentFlag is False):\n",
    "            if re.search(\"Resource Power Manager Stats\", line):\n",
    "                wakeupContentFlag = True\n",
    "\n",
    "        # Judge out app status\n",
    "        if (wakeupContentOutFlag is False) and (wakeupContentFlag is True):\n",
    "            if re.search(\"Total cpu time reads: \", line):\n",
    "                wakeupContentFlag = False\n",
    "                wakeupContentOutFlag = True\n",
    "\n",
    "\n",
    "\n",
    "        if batteryContentFlag:\n",
    "            if line.split():\n",
    "                tmp = parse_time(line.split()[0])\n",
    "                if tmp is not None:\n",
    "                    time = tmp\n",
    "\n",
    "            # 计算真实耗电量\n",
    "            charge_search = re.search(r'charge=(\\d+)', line)\n",
    "            if charge_search:\n",
    "                charge = int(charge_search.group(1))\n",
    "                charge_time = time\n",
    "                if charge_time is not None and charge_time > 3 * HOUR + 59 * MINUTE:\n",
    "                    screen_on_time = charge_time\n",
    "                    charge_end = charge\n",
    "                    batteryContentFlag = False\n",
    "                    batteryContentOutFlag = True\n",
    "\n",
    "            # 当灭屏后，flag标志位置为true，记录第一个charge\n",
    "            if charge_start_flag:\n",
    "                charge_start_flag = False\n",
    "                charge_start = charge\n",
    "\n",
    "            if \"-screen\" in line and screen_off_time is None:\n",
    "                screen_off_time = time\n",
    "                charge_start_flag = True\n",
    "\n",
    "\n",
    "\n",
    "            # 统计qpnp_rtc_alarm\n",
    "            parsed_line = parse_log_line(line)\n",
    "\n",
    "\n",
    "            m,n=count_alarm_walarm(line)\n",
    "            w_count += m\n",
    "            a_count += n\n",
    "            if parsed_line is None:\n",
    "                # 匹配qpnp_rtc_alarm\n",
    "                rtc_match = re.search(r'qpnp_rtc_alarm:.*\\((\\d+) times\\)', line)\n",
    "                if rtc_match:\n",
    "                    qpnp_rtc_alarm = int(rtc_match.group(1))\n",
    "\n",
    "\n",
    "                # 匹配actual drain\n",
    "                # Match the actual drain numbers and calculate the average\n",
    "                drain_match = re.search(r'actual drain: (\\d+\\.?\\d*)-(\\d+\\.?\\d*)', line)\n",
    "                if drain_match:\n",
    "                    num1 = float(drain_match.group(1))\n",
    "                    num2 = float(drain_match.group(2))\n",
    "                    atc_drain = (num1 + num2) / 2\n",
    "\n",
    "                # Match the durations\n",
    "                duration_matches = re.search(r'idle:.*duration: ([\\dhms\\. ]+)', line)\n",
    "                if duration_matches:\n",
    "                    duration = duration_matches.group(1)\n",
    "                continue\n",
    "\n",
    "\n",
    "            timestamp, event = parsed_line\n",
    "\n",
    "            if 'wake_reason=0:\"546 qpnp_rtc_alarm\"' in event:\n",
    "                prev_timestamp = timestamp\n",
    "            elif prev_timestamp is not None and '*walarm*:' in event and abs(prev_timestamp - timestamp) < timedelta(\n",
    "                    seconds=0.5):\n",
    "                walarm_rtc_count += 1\n",
    "                prev_timestamp = None\n",
    "            elif prev_timestamp is not None and '*alarm*:' in event and abs(prev_timestamp - timestamp) < timedelta(\n",
    "                    seconds=0.5):\n",
    "                alarm_rtc_count += 1\n",
    "                prev_timestamp = None\n",
    "\n",
    "        if wakeupContentFlag:\n",
    "            # 统计App wakeup alarm\n",
    "            # if \"Wakeup alarm *walarm*:\" in line:\n",
    "\n",
    "            # 根据apk名称统计\n",
    "            app_name_pattern = re.search(r'Apk\\s(.+?):',line)\n",
    "            if app_name_pattern:\n",
    "                if app_wakeup_num != 0:\n",
    "                    app_wakeup[app_name] = attri_num\n",
    "                app_wakeup_num = 0\n",
    "                app_name = app_name_pattern.group(1)\n",
    "            s_app_wakeup = re.search(r'Wakeup alarm \\*walarm\\*:(.+?):\\s(\\d+)\\s+times', line)\n",
    "            if s_app_wakeup:\n",
    "                attri = s_app_wakeup.group((1))\n",
    "                attri_num = int(s_app_wakeup.group((2)))\n",
    "                app_wakeup_alarm_num += attri_num\n",
    "                app_wakeup_num += attri_num\n",
    "        if wakeupContentOutFlag :\n",
    "            if app_wakeup_num != 0:\n",
    "                app_wakeup[app_name] = attri_num\n",
    "            break\n",
    "\n",
    "\n",
    "            # TODO:统计不同应用的waekup alarm，找到与qpnp_rtc_alarm关系最大的那个\n",
    "\n",
    "    print(\"App wakeup alarm：\"+str(app_wakeup_alarm_num))\n",
    "\n",
    "    # 增加属性\n",
    "    app_wakeup[\"wakeup_alarm\"] = app_wakeup_alarm_num\n",
    "    app_wakeup[\"qpnp_rtc_alarm\"] = qpnp_rtc_alarm\n",
    "\n",
    "    # 将文件中的wakeup数量增加到全局变量中\n",
    "    global all_attri_num\n",
    "    all_attri_num = pd.concat([all_attri_num, app_wakeup], axis=0, ignore_index=True)\n",
    "\n",
    "    if charge_end == None:\n",
    "        charge_end = charge\n",
    "    if screen_on_time == None:\n",
    "        screen_on_time = charge_time\n",
    "    if screen_off_time is None:\n",
    "        total_hour = screen_on_time / (60 * 60 * 1000)\n",
    "    else:\n",
    "        total_hour = (screen_on_time - screen_off_time) / (60 * 60 * 1000)\n",
    "    total_drain = charge_start - charge_end\n",
    "    avg_drain = total_drain / total_hour\n",
    "    return walarm_rtc_count + alarm_rtc_count, w_count + a_count,qpnp_rtc_alarm,atc_drain,duration,avg_drain,total_hour\n",
    "\n",
    "\n",
    "def print_hi(name):\n",
    "    # Use a breakpoint in the code line below to debug your script.\n",
    "    print(f'Hi, {name}')  # Press Ctrl+F8 to toggle the breakpoint.\n",
    "\n",
    "def count_Walarm_Qpnp(path):\n",
    "    fileContent = parse_log(path)\n",
    "    walarm_alarm_rtc_count,w_a_count,rtc_count,actual_drain,duration,avg_drain,test_time = count_event(fileContent)\n",
    "    print(\"test duration:\" + duration)\n",
    "    print(\"test time:\" + str(test_time)+\"h\")\n",
    "    # print(\"qpnp_rtc_alarm:\" + str(rtc_count))\n",
    "    # print(\"actual drain:\" + f\"{actual_drain:.2f}\")\n",
    "    print(\"walarm&rtc_alarm Count+alarm&rtc_alarm Count:\" + str(walarm_alarm_rtc_count) )\n",
    "    print(\"alarm Count + walarm Count:\" + str(w_a_count))\n",
    "    print(\"average drain:\" + f\"{avg_drain:.2f}\"+\"\\n\")\n",
    "    \n",
    "    if test_time < 3.9:\n",
    "        return None,None\n",
    "    return walarm_alarm_rtc_count, avg_drain\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "322f1b29",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "fullAlign\n"
     ]
    },
    {
     "ename": "FileNotFoundError",
     "evalue": "[Errno 2] No such file or directory: 'batteryStats/oppo/v0.1/batch/flight/fullAlign'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mFileNotFoundError\u001b[0m                         Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[2], line 20\u001b[0m\n\u001b[1;32m     18\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(\u001b[38;5;28mlen\u001b[39m(strategies_list)):\n\u001b[1;32m     19\u001b[0m     \u001b[38;5;28mprint\u001b[39m(strategies_list[i])\n\u001b[0;32m---> 20\u001b[0m     \u001b[38;5;28;01mfor\u001b[39;00m file_name \u001b[38;5;129;01min\u001b[39;00m \u001b[43mos\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mlistdir\u001b[49m\u001b[43m(\u001b[49m\u001b[43mstrtegies_path\u001b[49m\u001b[43m[\u001b[49m\u001b[43mi\u001b[49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m:\n\u001b[1;32m     21\u001b[0m         \u001b[38;5;28mprint\u001b[39m(file_name \u001b[38;5;241m+\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m: \u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m     22\u001b[0m         rtc_alarm,drain\u001b[38;5;241m=\u001b[39mcount_Walarm_Qpnp(os\u001b[38;5;241m.\u001b[39mpath\u001b[38;5;241m.\u001b[39mjoin(strtegies_path[i], file_name))\n",
      "\u001b[0;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: 'batteryStats/oppo/v0.1/batch/flight/fullAlign'"
     ]
    }
   ],
   "source": [
    "# 可选参数flight 、 wifi 、lte\n",
    "for network_pattern in networks:\n",
    "    # fullAlign对应完整的预测模块，align对应部分预测模块，paper对应对比论文，origin对应原生\n",
    "    data_path = \"batteryStats/oppo/v0.1/batch\"\n",
    "\n",
    "    # 将batterystats的txt文件放入下列路径中\n",
    "    fullAlign_path = os.path.join(data_path, network_pattern, strategies_list[0])\n",
    "    align_path = os.path.join(data_path, network_pattern, strategies_list[1])\n",
    "    paper_path = os.path.join(data_path, network_pattern, strategies_list[2])\n",
    "    origin_path = os.path.join(data_path, network_pattern, strategies_list[3])\n",
    "    # align_path = \"batteryStats/align\"\n",
    "    # origin_path = \"batteryStats/origin\"\n",
    "\n",
    "    strtegies_path = [fullAlign_path,align_path,paper_path,origin_path]\n",
    "\n",
    "\n",
    "\n",
    "    for i in range(len(strategies_list)):\n",
    "        print(strategies_list[i])\n",
    "        for file_name in os.listdir(strtegies_path[i]):\n",
    "            print(file_name + \": \")\n",
    "            rtc_alarm,drain=count_Walarm_Qpnp(os.path.join(strtegies_path[i], file_name))\n",
    "            # 添加数据\n",
    "            if rtc_alarm == None or drain == None:\n",
    "                    continue\n",
    "            network_data.get(network_pattern).get(strategies_list[i]).get(\"rtc_alarm\").append(rtc_alarm)\n",
    "            network_data.get(network_pattern).get(strategies_list[i]).get(\"drain\").append(drain)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "608fbe7c",
   "metadata": {},
   "outputs": [],
   "source": [
    "network_data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0a3c68a4",
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "attri = [\"rtc_alarm\",\"drain\"]\n",
    "\n",
    "# 创建一个2行2列的子图\n",
    "fig, axs = plt.subplots(2, 3)\n",
    "\n",
    "for i in range(len(attri)):\n",
    "    for j in range(len(networks)):\n",
    "        axs[i,j].boxplot([network_data.get(networks[j]).get(strategies_list[0]).get(attri[i]),\n",
    "                     network_data.get(networks[j]).get(strategies_list[3]).get(attri[i])],showmeans=True,meanline=True)\n",
    "        axs[i,j].set_xticklabels([strategies_list[0], strategies_list[3]])\n",
    "        axs[i,j].set_title(networks[j]+\" \"+attri[i])\n",
    "\n",
    "# 调整子图之间的垂直间距\n",
    "plt.subplots_adjust(hspace=0.5)\n",
    "# 显示图形\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bb2be53a",
   "metadata": {},
   "outputs": [],
   "source": [
    "network_data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "23a25070",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "def calculate_stats(arr):\n",
    "    mean = np.mean(arr)\n",
    "    std = np.std(arr)\n",
    "    median = np.median(arr)\n",
    "    return mean, std, median\n",
    "\n",
    "arr = [1, 2, 3, 4, 5]\n",
    "\n",
    "\n",
    "for j in range(len(networks)):\n",
    "    align_drain = network_data.get(networks[j]).get(strategies_list[0]).get(attri[1])\n",
    "    origin_drain = network_data.get(networks[j]).get(strategies_list[3]).get(attri[1])\n",
    "    mean, std, median = calculate_stats(align_drain)\n",
    "    print(\"\\n\"+networks[j])\n",
    "    print(\"Align\")\n",
    "    print(f'平均值: {mean}')\n",
    "    print(f'标准差: {std}')\n",
    "    print(f'中位数: {median}')\n",
    "    origin_mean, origin_std, origin_median = calculate_stats(origin_drain)\n",
    "    print(\"Origin\")\n",
    "    m = -(origin_mean - mean)/origin_mean * 100\n",
    "    n = -(origin_std -std)/origin_std * 100\n",
    "    q = -(origin_median -median)/origin_median * 100\n",
    "    print(f'平均值: {origin_mean}')\n",
    "    print(f'标准差: {origin_std}')\n",
    "    print(f'中位数: {origin_median}')\n",
    "    \n",
    "    print(f'\\n下降比例: {m}%')\n",
    "    print(f'下降比例: {n}%')\n",
    "    print(f'下降比例: {q}%')\n",
    "    \n",
    "    \n",
    "                                                                             "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6b419fb7",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.16"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
